from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.views.generic import list_detail
from django.template import loader as default_loader
from django.conf import settings
from django.db import models
from django import forms

COLL_MAP = {
    'GET': 'list',
    'POST': 'create'
    }

ENTRY_MAP = {
    'GET': 'retrieve',
    'PUT': 'update',
    'DELETE': 'delete'
}

class Collection(object):
    def __call__(self, request, id=None, noun=None):
        self.request = request
        self.id = id

        # simulate DELETE and PUT method using POST.
        # useful when client is browser.
        # can't set request.method , so have to use sim_method instead ;-(
        request.sim_method = request.method
        if request.method == 'POST': 
            if '_delete' in request.POST:
                request.sim_method = 'DELETE'
            elif '_put' in request.POST:
                request.sim_method = 'PUT'
                request.PUT = request.POST

        method = request.sim_method

        function_name = "%s_%s" % (method.lower(), noun)
        if not noun:
            method_map = id and ENTRY_MAP or COLL_MAP
            function_name = method_map.get(method, '') 

        if function_name and not function_name.startswith("_") and function_name in dir(self):
            # used by django debug page
            self.func_name = self.__class__.__name__+'.'+function_name
            return getattr(self, function_name)()
        else:
            print function_name
            raise Http404

class GenericCollection(Collection):
    def __init__(self, paginate_by=15, template_dir=None,
            template_loader=None, allow_empty=False,
            context_processors=None, mimetype=None, extra_context=None):
        self.paginate_by = paginate_by
        self.template_dir = template_dir or ''
        self.template_loader = template_loader or default_loader
        self.allow_empty = allow_empty
        self.context_processors = context_processors or []
        self.mimetype = mimetype or settings.DEFAULT_CONTENT_TYPE
        self.extra_context = extra_context

    def __call__(self, request, app, model, id=None, noun=None, **kw):
        self.__dict__.update(kw)
        self.model = models.get_model(app, model)
        if not self.model:
            raise Http404
        self.model_name = model
        self.template_object_name = model
        self.template_dir = self.template_dir or app
        return super(GenericCollection, self).__call__(request, id, noun)

    def list(self):
        template_name = '%s/%s_list.html' % ( self.template_dir, self.model_name )
        return list_detail.object_list(self.request,
                queryset=self.model.objects.all(),
                paginate_by=self.paginate_by,
                template_name=template_name,
                template_loader=self.template_loader,
                extra_context=self.extra_context,
                allow_empty=self.allow_empty,
                context_processors=self.context_processors,
                template_object_name=self.template_object_name,
                mimetype=self.mimetype
        )

    def create(self):
        manipulator = self.model.AddManipulator()
        new_data = self.request.POST.copy()
        errors = manipulator.get_validation_errors(new_data)
        if not errors:
            manipulator.do_html2python(new_data)
            new_object = manipulator.save(new_data)
            return HttpResponseRedirect('%d/;update_form/'%new_object.id)
        form = forms.FormWrapper(manipulator, new_data, errors)
        template_name = '%s/%s_form.html' % ( self.template_dir, self.model_name )
        return render_to_response(template_name, {'form': form})

    def retrieve(self):
        template_name = '%s/%s_detail.html' % ( self.template_dir, self.model_name )
        return list_detail.object_detail(self.request,
                queryset=self.model.objects.all(),
                object_id=self.id,
                template_name=template_name,
                template_loader=self.template_loader,
                extra_context=self.extra_context,
                context_processors=self.context_processors,
                template_object_name=self.template_object_name,
                mimetype=self.mimetype
        )

    def update(self):
        assert self.request.sim_method == 'PUT'
        try:
            manipulator = self.model.ChangeManipulator(self.id)
        except self.model.DoesNotExist:
            raise Http404

        new_data = self.request.PUT.copy()
        errors = manipulator.get_validation_errors(new_data)
        if not errors:
            manipulator.do_html2python(new_data)
            manipulator.save(new_data)
            return HttpResponseRedirect(';update_form')

        template_name = '%s/%s_form.html' % ( self.template_dir, self.model_name )
        form = forms.FormWrapper(manipulator, new_data, errors)
        return render_to_response(template_name, {'form':form})

    def delete(self):
        self.model.objects.filter(id=self.id).delete()
        return HttpResponseRedirect('..')

    def get_create_form(self):
        form = forms.FormWrapper(self.model.AddManipulator(), {}, {})
        template_name = '%s/%s_form.html' % ( self.template_dir, self.model_name )
        return render_to_response(template_name, {'form':form})

    def get_update_form(self):
        try:
            manipulator = self.model.ChangeManipulator(self.id)
        except self.model.DoesNotExist:
            raise Http404

        new_data = manipulator.flatten_data()
        form = forms.FormWrapper(manipulator, new_data, {})
        template_name = '%s/%s_form.html' % ( self.template_dir, self.model_name )
        return render_to_response(template_name, 
                {'form':form, 'update':True, 
                    self.template_object_name:manipulator.original_object})

